﻿using System;
using System.Configuration;
using System.Data.SqlClient;
using System.Data;

namespace Tamias.DefaultImplementation.Data
{
    public abstract class DataClass
    {
        #region Constructor

        public DataClass() { }


        #endregion
        #region Connections

        protected SqlConnection CreateUnopenedConnection()
        {
            const string name = "Tamias";
            if (ConfigurationManager.ConnectionStrings[name] == null)
                throw new Exception("For the default implementation, you need a connection string setup in the connection strings called 'Tamias'.");

            string connectionString = ConfigurationManager.ConnectionStrings[name].ConnectionString;

            SqlConnection conn = new SqlConnection(connectionString);
            return conn;
        }

        #endregion

        protected SqlDataReader ExecuteReader(string sql)
        {
            return ExecuteReader(sql, null);
        }

        protected SqlDataReader ExecuteReader(string sql, ParameterCollection parameters)
        {
            SqlCommand cmd = new SqlCommand(sql, CreateUnopenedConnection());
            cmd.CommandType = CommandType.Text;

            if (parameters != null)
            {
                foreach (SqlParameter param in parameters)
                    cmd.Parameters.Add(param);
            }

            cmd.Connection.Open();
            try
            {
                return cmd.ExecuteReader(CommandBehavior.CloseConnection);
            }
            catch (SqlException exc)
            {
                var typedException = CreateTypedException(exc);
                if (typedException != null)
                    throw typedException;
                else
                    throw;
            }
        }

        protected int ExecuteNonQuery(string sql, ParameterCollection parameters)
        {
            SqlCommand cmd = CreateCommand(sql, parameters);

            using (SqlConnection conn = CreateUnopenedConnection())
            {
                cmd.Connection = conn;
                conn.Open();
                try
                {
                    return cmd.ExecuteNonQuery();
                }
                catch (SqlException exc)
                {
                    var typedException = CreateTypedException(exc);
                    if (typedException != null)
                        throw typedException;
                    else
                        throw;
                }

            }
        }

        private SqlCommand CreateCommand(string sql, ParameterCollection parameters)
        {
            SqlCommand cmd = new SqlCommand(sql);
            if (parameters != null)
                foreach (SqlParameter param in parameters)
                    cmd.Parameters.Add(param);
            return cmd;
        }

        protected int ExecuteIdentityInsert(string sql, ParameterCollection parameters)
        {
            SqlCommand cmd = new SqlCommand(sql);
            if (parameters != null)
                foreach (SqlParameter param in parameters)
                    cmd.Parameters.Add(param);

            using (SqlConnection conn = CreateUnopenedConnection())
            {
                cmd.Connection = conn;
                conn.Open();
                try
                {
                    object returnValue = cmd.ExecuteScalar();
                    return Convert.ToInt32(returnValue);
                }
                catch (SqlException exc)
                {
                    var typedException = CreateTypedException(exc);
                    if (typedException != null)
                        throw typedException;
                    else
                        throw;
                }
            }
        }

        private Exception CreateTypedException(SqlException exc)
        {
            bool isDefined = Enum.IsDefined(typeof(SqlErrorNumber), exc.Number);

            if (!isDefined)
                return null;

            var errorNumber = (SqlErrorNumber)exc.Number;
            switch (errorNumber)
            {
                case SqlErrorNumber.DuplicateKeyException:
                    return new DuplicateKeyException();
                case SqlErrorNumber.UniqueConstraintViolation:
                    return new UniqueConstraintViolationException();
            }

            return null;
        }
    }
}
